/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2024年8月13日
 * V4.0
 */
package com.jphenix.servlet.spring.impl;
//#region 【引用区】
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jphenix.kernel.objectloader.interfaceclass.IBeanFactory;
import com.jphenix.servlet.filter.BaseFilter;
import com.jphenix.servlet.interfaceclass.IFilterInitEvent;
import com.jphenix.servlet.spring.JPhenixSpring;
import com.jphenix.servlet.spring.JPhenixSpringException;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.util.ClassUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.script.IScriptLoader;
//#endregion

//#region 【说明区】
/**
 * 与Spring互动的主实现类
 * com.jphenix.servlet.spring.impl.JPhenixSpringImpl
 * 
 * ----------------------------------------------------------
 * 
 * Spring中的配置参数：
 * 
 *   jphenix.disabled  是否禁用当前架构
 *   
 *   
 * ----------------------------------------------------------
 * 
 * 注意：允许Spring调用当前架构中的资源，禁止当前架构中的资源（包括内外脚本）调用Spring中的资源！
 * 
 * 如果当前架构中的脚本需要调用Spring项目中的资源，建议采用http协议调用controller方式，或者把Spring中
 * 需要调用的功能迁移到当前架构中。
 * 
 * 为什么禁止当前架构资源调用Spring资源？ 因为Spring能做到的，当前架构都能做到。当前架构嵌入Spring中只是为了
 * 弥补Spring架构的不足，提高开发效率和维护效率。
 * 
 * 在程序后期，嵌入Spring中的当前架构可以完全从Spring中剥离出来独立运行，并且能确保基于当前架构中的功能都是正常的。
 * 一旦做了双向交互。当前架构就无法从Spring中剥离出来了，这就失去了当前架构嵌入Spring中的主要意义。
 * 
 * 什么主要意义？这个不能说，总之将当前架构嵌入Spring中后，就会发现真香，以后一直香
 * 
 * 注意：Spring中调用脚本，方法中并没有声明抛出异常，实际会抛出 RuntimeException，这也是顺应Spring开发风格的尿性.
 * 
 * ----------------------------------------------------------
 * 
 * @author MBG
 * 2024年8月13日
 */
//#endregion
@Configuration
@Service
@ClassInfo({"2024-08-17 20:45","与Spring互动的主实现类"})
public class JPhenixSpringImpl implements JPhenixSpring,IFilterInitEvent {
	
	//#region 【声明区】
	
	// Spring 的日志类（slf4j）
	private static final Logger log = LoggerFactory.getLogger(JPhenixSpringImpl.class);
	
	// 参数主键前缀
	private final String PROPERTY_PREFIX = "jphenix.";
	
	private BaseFilter baseFilter = null; // 根过滤器
	
    // 类加载器
    @Resource
    private ApplicationContext applicationContext;

    // 参数处理类
    private Environment env = null;
	//#endregion
    
    //#region getProperty(key,defaultValue) 从Spring架构中获取参数值
    /**
     * 从Spring架构中获取参数值
     * @param key              参数主键（注意：实际参数主键全部都带前缀 PROPERTY_PREFIX 的值）
     * @param defaultValue     默认值（如果参数值为null或空字符串，则返回该值）
     * @return                 参数值
     */
    public String getProperty(String key,String defaultValue) {
    	if(defaultValue==null) {
    		defaultValue = "";
    	}
    	if(key==null || key.length()<1) {
    		return defaultValue;
    	}
    	if(key.startsWith(PROPERTY_PREFIX)) {
    		key = key.substring(PROPERTY_PREFIX.length());
    	}
    	if(env==null) {
    		env = applicationContext.getBean(Environment.class);
    	}
    	// 获取参数值
    	String res = env.getProperty(PROPERTY_PREFIX+key);
    	if(res==null || res.length()<1) {
    		log.debug("--JPhenix getProperty("+key+") Return DefaultValue:["+defaultValue+"]"); 
    		return defaultValue;
    	}
    	log.debug("--JPhenix getProperty("+key+") Return Value:["+res+"]"); 
    	return res;
    }
    //#endregion
    
    //#region getProperty(key) 从Spring架构中获取参数值
    /**
     * 从Spring架构中获取参数值
     * @param key 参数主键（注意：实际参数主键全部都带前缀 PROPERTY_PREFIX 的值）
     * @return    参数值
     */
    public String getProperty(String key) {
    	return getProperty(key,null);
    }
	//#endregion
    
	//#region getFilter() 获取当前框架的主过滤器实例
	/**
	 * 获取当前框架的主过滤器实例
	 * @return 当前框架的主过滤器实例
	 */
	public BaseFilter getFilter() {
		return baseFilter;
	}
	//#endregion
	
	//#region getBeanFactory() 获取当前框架的基础类加载器实例
	/**
	 * 获取当前框架的基础类加载器实例
	 * @return 当前框架的基础类加载器实例
	 */
	public IBeanFactory getBeanFactory() {
		if(baseFilter==null) {
			return null;
		}
		return baseFilter.getBeanFactory();
	}
	//#endregion
	
	//#region getScriptLoader() 获取当前架构中的脚本加载器实例
	/**
	 * 获取当前架构中的脚本加载器实例
	 * @return 当前架构中的脚本加载器实例
	 */
	public IScriptLoader getScriptLoader() {
		if(baseFilter==null) {
			return null;
		}
		try {
			return baseFilter.getScriptLoader();
		}catch(Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	//#endregion
	
	//#region call(scriptId,paraMap,invoker) 调用目标脚本并获取返回值
	/**
	 * 调用目标脚本并获取返回值
	 * @param scriptId    脚本代码
	 * @param paraMap     入参容器（具体入参，见对应脚本配置）
	 * @param invoker     调用者（通常传入this，用于排查问题）
	 * @return            调用结果
	 */
	public Object call(String scriptId,Map<String,?> paraMap,Object invoker){
		// 获取类加载器
		IScriptLoader sl = getScriptLoader();
		if(sl==null) {
			return null;
		}
		try {
			return sl.invokeScript(invoker,scriptId,paraMap);
		}catch(Exception e) {
			e.printStackTrace();
			throw new JPhenixSpringException(e);
		}
	}
	//#endregion
	
	//#region call(scriptId,invoker) 调用目标脚本并获取返回值
	/**
	 * 调用目标脚本并获取返回值
	 * @param scriptId    脚本代码
	 * @param invoker     调用者（通常传入this，用于排查问题）
	 * @return            调用结果
	 */
	public Object call(String scriptId,Object invoker){
		// 获取类加载器
		IScriptLoader sl = getScriptLoader();
		if(sl==null) {
			return null;
		}
		try {
			return sl.invokeScript(invoker,scriptId);
		}catch(Exception e) {
			e.printStackTrace();
			throw new JPhenixSpringException(e);
		}
	}
	//#endregion
	
	//#region getBean() 注册架构中的过滤器
	/**
	 * 注册架构中的过滤器
	 * @return 注册类实例
	 * 2024年8月13日
	 * @author MBG
	 */
	@SuppressWarnings({ "rawtypes" })
	@Bean()
    public FilterRegistrationBean getBean() {

		// 当前架构是否使用的是javax的Servlet-api，或者是 jakarta的（Tomcat10以后的都是这个）
		boolean javaxFilter = ClassUtil.exists("javax.servlet.Filter");
		
		try {
			
			//#region 是否禁用当前SDK
			if(SBoolean.valueOf(getProperty("disabled"))) {
				
				log.warn(" ## JPhenix SDK Has Disabled ## ("+PROPERTY_PREFIX+"enabled:false)");
				
				// javax 的 Servlet-api
		    	if(javaxFilter) {
		    		return getFilterByClassPath("com.jphenix.servlet.api.javax.EmptyFilter");
		    	}
		    	// jakarta 的 Servlet-api
		    	return getFilterByClassPath("com.jphenix.servlet.api.jakarta.EmptyFilter");
			}
			//#endregion
			
			log.info(" ## Begin Load JPhenix SDK ##");
			
			// javax 的 Servlet-api
	    	if(javaxFilter) {
	    		return getFilterByClassPath("com.jphenix.servlet.api.javax.FilterExplorer");
	    	}
	    	// jakarta 的 Servlet-api
	    	return getFilterByClassPath("com.jphenix.servlet.api.jakarta.FilterExplorer");
	    	
		}catch(Exception e) {
			log.error("## Load JPhenix SDK Exception",e);
			throw new JPhenixSpringException(e);
		}
    }
    //#endregion
	
	//#region 【内部方法】getFilterByClassPath(classPath) 通过类路径构造过滤器并返回
	/**
	 * 通过类路径构造过滤器并返回
	 * @param classPath  过滤器类路径
	 * @return           过滤器注册信息类
	 * @throws Exception 异常
	 */
	@SuppressWarnings("rawtypes")
	private FilterRegistrationBean getFilterByClassPath(String classPath) throws Exception {

		// 指定类名的 servlet-api
		baseFilter = (BaseFilter)Class.forName(classPath).newInstance();
    	
		//#region 构建返回值
		FilterRegistrationBean<?> bean = new FilterRegistrationBean();
        bean.setOrder(-1);
        bean.setUrlPatterns(Arrays.asList("*.ha"));
        
        Method setMethod = null; // 构建设置过滤器方法对象
		Method[] method = bean.getClass().getDeclaredMethods();
		for(int i=0;i<method.length;i++) {
			if(method[i]!=null && method[i].getName().equals("setFilter")) {
				setMethod = method[i];
				break;
			}
		}
		if(setMethod==null) {
			log.error("XXXX Error: The JPhenix SDK Regist Filter Not Find The Method:setFilter XXXX");
			return null;
		}

		setMethod.invoke(bean,new Object[] {baseFilter});
		//#endregion

        return bean;
    }
	//#endregion

	//#region afterInit(bf) 在过滤器初始化完毕后回调该方法
	/**
	 * 在过滤器初始化完毕后回调该方法
	 * @param bf 根过滤器实例
	 */
	@Override
	public void afterInit(BaseFilter bf) {
		try {
			bf.getBeanFactory().setObject(JPhenixSpring.class,this);
		}catch(Exception e) {
			e.printStackTrace();
			log.error("Invoke afterInit Method Exception",e);
		}
	}
	//#endregion
}










